#include "../include/defs.h"
#include "../include/kc.h"
#include "../include/sleep.h"
#include "../include/random.h"
#define CUANTUM_MAX 6

extern PROCESO procesos[];
extern int pidActual;
extern peticion peticionesDormir[];
extern  int maxmem;

int indiceX = 0;
int ejecutados[100] = { 0 };
int nivelActual = 0;
int actual = 0;
int vuelta = 0;

extern unsigned long *page_table;
extern unsigned long *page_directory;

void
SaveESP (int ESP) {

    PROCESO *temporal;
    temporal = GetProcessByPid (pidActual);
    temporal->ESP = ESP;
    return;
}

int

bajarPaginasAnterior (int pid) {
    PROCESO *myProcess;
    int io, il,j,i;


   if( pid == 0 )
     return 0;

    myProcess = GetProcessByPid (pid);

    if( pid == TEMP )
     {
	bajarPaginas(NULL);
     }
    
    bajarPaginas(myProcess);
    
    return 0;

}

int
levantaPaginasNuevo (int pid) {


    PROCESO *myProcess;
    int io, il,j,i;
     

    if( pid == 0 )
     return 0;

    myProcess = GetProcessByPid (pid);
       
       /* Temp es de temporal, se usa por lo pronto para levantar gran parte de las paginas ya que en creaProceso se llama a malloc y este esta tocando la memoria de 
       alguna manera, ergo explota si no estan levantadas */
       
    if( pid == TEMP  )
     {
        levantaPaginas(NULL);
     }
     
     levantaPaginas(myProcess);
   
    return 0;
}



int bajarPaginas(PROCESO * myProcess)
{
    int io, il,j,i;
     
      if( myProcess == NULL  )
     {
       io = INITIAL_PAGE;
       il = maxmem/4; // Da la ultima pagina a levantar
     }
     else{
      
 	   io =  (int) (myProcess->heapstart -
                               (myProcess->stacksize +
                                myProcess->heapsize)) / 4096 ;
   	   il =  (int) myProcess->heapstart / 4096  ;
   	   
	 }  
	 
   

   /* La posicion de la pagina dividido 1024 nos da la posicion en la tabla
de directorio de la tabla de paginas a la cual pertenece esa pagina */
    for( i = io/1024; i <= il/1024 ; i++)
    {
      int upTo;
      
      page_table = page_directory + 4096 * (i+1);
     
      if( i != io/1024 )
          j = 0;
      else 
          j = io%1024;
           
     
      if( i == il/1024) 
       upTo = (il-io)%1024+io%1024;
      else upTo = 1024;
       
       
       for(; j <  upTo ; j++)
         page_table[j] = page_table[j] & 0xFFFFFFFE;
    
       page_directory[i] = (unsigned long)page_table;
       page_directory[i] = page_directory[i] & 0xFFFFFFFE;;

     } 
    
    
    return 0;
   
}


int
levantaPaginas(PROCESO * myProcess) {

    int io, il,j,i;
   
   
     
   if( myProcess == NULL  )
     {
       io = INITIAL_PAGE;
       il = maxmem/4; // Da la ultima pagina a levantar
     }
     else{
      
 	   io =  (int) (myProcess->heapstart -
                               (myProcess->stacksize +
                                myProcess->heapsize)) / 4096;
   	   il =  (int) myProcess->heapstart / 4096 ;
   	   
	 }  
	 
   

   /* La posicion de la pagina dividido 1024 nos da la posicion en la tabla
de directorio de la tabla de paginas a la cual pertenece esa pagina */
    for( i = io/1024; i <= il/1024 ; i++)
    {
      int upTo;
      
      page_table = page_directory + 4096 * (i+1);
     
      if( i != io/1024 )
          j = 0;
      else 
          j = io%1024;
           
     
      if( i == il/1024) 
       upTo = (il-io)%1024+io%1024;
      else upTo = 1024;
       
       
       for(; j <  upTo ; j++)
         page_table[j] = page_table[j] | 7;
    
       page_directory[i] = (unsigned long)page_table;
       page_directory[i] = page_directory[i] | 7;

     } 
    
    
    return 0;
}



/* siguiente proceso a ejecutar */
int
GetNextProcess (int esp) {
    PROCESO *temporal;
    SaveESP (esp);
    temporal = GetNextTask ();
  
    bajarPaginasAnterior(pidActual);
   
    pidActual = temporal->pid;
   
    levantaPaginasNuevo(pidActual);
    return temporal->ESP;
}

int
contarProcesos(void)
{
    int resp=0;
    int i;
    for(i=0;i<MAXPROCESOS;i++)
    {
        if(!procesos[i].free_slot && procesos[i].bloqueado==0&& i!=GOD)
        {
            resp++; 
        }
    }
    return resp;
}

/* la papota */
#define CANT_MAX_ 5
int anteriorP=0;
int mayor=1;
int vueltas=0;
PROCESO *
GetNextTask (void) {
    int p1=0;
    int p2=0;
    int condicion=1;
    int primero=1;
    int segundo=1;
    int i;
    int cantidadP; 
    int salir=0;
    int cant;

    for (i = 0; i < MAXPROCESOS; i++) {
        if (peticionesDormir[i].time != 0) {
            if (--peticionesDormir[i].time == 0)
                desbloqueaProceso (peticionesDormir[i].pid);
        }
    }
    
    if (NoHayProcesos ()) {
        ejecutados[indiceX] = GOD;
        indiceX = (indiceX + 1) % 100;
        return &procesos[GOD];
    }
    /*Cuento entre cuantos debo hacer la loteria*/
    cantidadP= contarProcesos();
    
    /*Para mi deberia ir esta condicion pero si la pongo se resetea bochs :-S */
    //while(p1!=0 && p2!=0)
    //{
        p1=random(cantidadP+1, &anteriorP);
        p2=random(cantidadP+1, &anteriorP); 
    //}
    /*Asigno los verdaderos i para cada uno*/
    
    for(i=0,cant=0;i<MAXPROCESOS &!salir;i++)
    {
        if(!procesos[i].free_slot && procesos[i].bloqueado==0&& i!=GOD)
        {
            cant++; 
            if(cant==p1)
            {
                p1=i;
                salir=1;
            }
            
        }
    }
    salir=0;
    cant=0;
    for(i=0,cant=0;i<MAXPROCESOS &!salir;i++)
    {
        if(!procesos[i].free_slot && procesos[i].bloqueado==0&& i!=GOD)
        {
            cant++; 
            if(cant==p2)
            {
                p2=i;
                salir=1;
            }
        }
    }

    /*Devuelvo cada uno depende si es mayor o menor*/
    if(mayor)
    {
        if(vueltas==CANT_MAX_)
        {
            vueltas=0;
            mayor=0;
        }
        else
            vuelta++;
        if(procesos[p2].prioridad>procesos[p1].prioridad)
        {

            ejecutados[indiceX] = p2;
            indiceX = (indiceX + 1) % 100;
            return &procesos[p2];
        }
        else
        {
            ejecutados[indiceX] = p1;
            indiceX = (indiceX + 1) % 100;
            return &procesos[p1];
        }
    }
    else
    {
        mayor=1;
        if(procesos[p2].prioridad<procesos[p1].prioridad)
        {

            ejecutados[indiceX] = p2;
            indiceX = (indiceX + 1) % 100;
            return &procesos[p2];
        }
        else
        {
            ejecutados[indiceX] = p1;
            indiceX = (indiceX + 1) % 100;
            return &procesos[p1];
        }
    }

    /*Si falla todo GOD will recue us*/
    ejecutados[indiceX] = GOD;
    indiceX = (indiceX + 1) % 100;
    return &procesos[GOD];
}

int
LoadESP (PROCESO * proc) {
    return proc->ESP;
}

void
initScheduler (void) {
    int i,j;

 //   void *stack = Malloc (512);
   /*Hay que busfcar una direccion perteneciente a la zona de kernel*/
    void *stack = 0x300000;
    
    /* todos los slots estan libres */
    for (i = 0; i < MAXPROCESOS; i++)
        procesos[i].free_slot = 1;

    /* no hay procesos en la lista de sleep */
    for (i = 0; i < MAXPROCESOS; i++)
        peticionesDormir[i].time = 0;
        
    /* Levantamos los ultimos 4MB de memoria para shared-mem */   
    
     for( i = maxmem/4096-1; i <= maxmem/4096-1; i++)
    {
      
       page_table = page_directory + 4096 * (i+1);
   
       for( j = 0 ; j < 1024 ; j++)
         page_table[j] = page_table[j] | 7;
    
       page_directory[i] = (unsigned long)page_table;
       page_directory[i] = page_directory[i] | 7;

     } 
     
     
     
        

}
